#include "DirectX.h"
#include "MemoryManager.h"
#include "ServiceLocator.h"
#include "Input.h"

#define HRESULT_CODE(hr)    ((hr) & 0xFFFF)

#pragma warning(disable: 4355)

DirectX::DirectX()
	: Service<DirectX>( this )
{
	m_x = 0;
	m_y = 0;
}
static bool flipped = false;
static bool moved = false;



DirectX::~DirectX()
{
	for( std::vector<sprite>::iterator iter(m_sprites.begin()), endIter(m_sprites.end()); iter != endIter; ++iter )
	{
		(*iter).m_sprite.Release();
	}
	m_declaration->Release();
	m_d3d->Release();
	m_d3ddev->Release();
}

bool DirectX::Init(HWND hWnd, UINT uiWidth, UINT uiHeight)
{
	m_uiWidth = uiWidth;
	m_uiHeight = uiHeight;
	m_fHeight = (float)uiHeight;
	m_fWidth = (float)uiWidth;

	m_d3d = Direct3DCreate9(D3D_SDK_VERSION);    // create the Direct3D interface

	ZeroMemory(&m_d3dpp, sizeof(m_d3dpp));    // clear out the struct for use
	m_d3dpp.Windowed = TRUE;    // program windowed, not fullscreen
	m_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // discard old frames
	m_d3dpp.hDeviceWindow = hWnd;    // set the window to be used by Direct3D
	m_d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;    // set the back buffer format to 32-bit
	m_d3dpp.BackBufferWidth = m_uiWidth;    // set the width of the buffer
	m_d3dpp.BackBufferHeight = m_uiHeight;    // set the height of the buffer

	// create a device class using this information and information from the d3dpp stuct
	if( m_d3d->CreateDevice(  D3DADAPTER_DEFAULT
							, D3DDEVTYPE_HAL
							, hWnd
							, D3DCREATE_SOFTWARE_VERTEXPROCESSING
							, &m_d3dpp
							, &m_d3ddev) != S_OK)
	{
		return false;
	}


	D3DVERTEXELEMENT9 decl[] =
	{
		{ 0,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
		{ 0, 8, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,   0 },
		D3DDECL_END( )
	};

	if( m_d3ddev->CreateVertexDeclaration(decl,&m_declaration) != S_OK)
	{
		return false;
	}

	animation.Init( LoadSprite(TEXT("../Data/Player_Run.png"), 64,64), 8, 5000);
	
	return true;
}

struct CUSTOMVERTEX
{
	//float x, y;    // from the D3DFVF_XYZRHW flag
	D3DXVECTOR2 vec;
	D3DXVECTOR2 texcoord;
};

void DirectX::render_frame( double frameTime )
{
	if( ServiceLocator::GetService<Input>()->GetInput(IA_Left) == IT_KeyDown )
	{
		m_x += -1;
		flipped = true;
	}

	if( ServiceLocator::GetService<Input>()->GetInput(IA_Right) == IT_KeyDown )
	{
		m_x += 1;
		flipped = false;
	}

	if( ServiceLocator::GetService<Input>()->GetInput(IA_Up) == IT_KeyDown )
	{
		m_y -= 1;
	}
	if( ServiceLocator::GetService<Input>()->GetInput(IA_Down) == IT_KeyDown )
	{
		m_y += 1;
	}
// 	m_x += x;
// 	m_y += y;
// 	if( x < 0 )
// 	{
// 		flipped = true;
// 	}
// 	if( x > 0 )
// 	{
// 		flipped = false;
// 	}

	//OutputDebugString(TEXT("render\n"));
// 	wchar_t szBuff[1024];
// 	wsprintf(szBuff, L"\n%d", (int)frameTime);
// 
// 	OutputDebugString(szBuff);


	static bool init(false);
	if( !init )
	{
		init = true;
		animation.StartAnimation((int)frameTime);
	}

	float triangleWidth(64.f / m_fWidth * 2.f);
	float triangleHeight(64.f / m_fHeight * 2.f);

	CUSTOMVERTEX OurVertices[] =
	{
		{D3DXVECTOR2(0.f,0.f), D3DXVECTOR2(0.f,1.f)},
		{D3DXVECTOR2(0.f,triangleHeight), D3DXVECTOR2(0.f,0.f)},
		{D3DXVECTOR2(triangleWidth,0.f), D3DXVECTOR2(1.f,1.f)},

		D3DXVECTOR2(0.0f,triangleHeight), D3DXVECTOR2(0.f,0.f),
		D3DXVECTOR2(triangleWidth,triangleHeight), D3DXVECTOR2(1.f,0.f),
		D3DXVECTOR2(triangleWidth,0.f), D3DXVECTOR2(1.f,1.f),
	};
	LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL; 
	// create a vertex buffer interface called v_buffer
	HRESULT res = m_d3ddev->CreateVertexBuffer(2*3*sizeof(CUSTOMVERTEX),
		0,
		0,
		D3DPOOL_MANAGED,
		&v_buffer,
		NULL);

	VOID* pVoid;    // a void pointer

	// lock v_buffer and load the vertices into it
	res = v_buffer->Lock(0, 0, (void**)&pVoid, 0);
	memcpy(pVoid, OurVertices, sizeof(OurVertices));
	v_buffer->Unlock();

	float rotation(45);

	D3DXMATRIX matRotation, matTranslate, matFinal;
	//x rotation (NEW)
	D3DXMatrixRotationZ(&matRotation, rotation * 0.0174532925f );

	D3DXMatrixTranslation(&matTranslate,100,100,1);
	//D3DXMatrixMultiply(&matFinal, &matRotation, &matTranslate);
	//y rotation (NEW)
	D3DXMatrixMultiply(&matFinal, &matRotation, &BuildProjectionMatrix());


	//MOVE ME
	m_SimpleSurfaceShader.Handles.MatrixHandle = m_SimpleSurfaceShader.Effect->GetParameterByName(0, "World");
	m_SimpleSurfaceShader.Handles.TextureHandle = m_SimpleSurfaceShader.Effect->GetParameterByName(0,"Sprite");


	m_d3ddev->SetVertexDeclaration(m_declaration);
	m_d3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
	m_d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
	//m_d3ddev->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
	// clear the window to a deep blue
	m_d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(100, 40, 250), 1.0f, 0);

	m_d3ddev->BeginScene();    // begins the 3D scene
	// select the vertex buffer to display
	m_d3ddev->SetStreamSource(0, v_buffer, 0, sizeof(CUSTOMVERTEX));
	m_SimpleSurfaceShader.Effect->SetMatrix(m_SimpleSurfaceShader.Handles.MatrixHandle,&matFinal );

 	m_SimpleSurfaceShader.Effect->SetTexture(m_SimpleSurfaceShader.Handles.TextureHandle, m_sprites[0].m_sprite.GetTexture());
// 
 	m_SimpleSurfaceShader.Effect->Begin(0,0);
	m_SimpleSurfaceShader.Effect->BeginPass(0);
	// copy the vertex buffer to the back buffer
	//res = m_d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST         , 0, 2);
// 	D3DXVECTOR3 pos;

// 	m_sprites[0].m_sprite.Draw( 0, 0, 200 + m_x, 200 + m_y);	
// 	m_sprites[0].m_sprite.Draw( 1, 0, 300, 200 );
// 	m_sprites[0].m_sprite.Draw( 2, 0, 200, 300 );	
// 	m_sprites[0].m_sprite.Draw( 3, 0, 300, 300 );
// 	m_sprites[0].m_sprite.Draw( 4, 0, 200, 400 );
// 	m_sprites[0].m_sprite.Draw( 5, 0, 300, 400 );
// 	m_sprites[0].m_sprite.Draw( 6, 0, 200, 500 );
// 	m_sprites[0].m_sprite.Draw( 7, 0, 300, 500 );
	animation.Draw( frameTime,0.f, 400+m_x, 400+m_y, flipped );
	
	m_SimpleSurfaceShader.Effect->EndPass();
	m_SimpleSurfaceShader.Effect->End();
	// do 3D rendering on the back buffer here
	m_d3ddev->EndScene();    // ends the 3D scene
	
	m_d3ddev->Present(NULL, NULL, NULL, NULL);    // displays the created frame]
	v_buffer->Release();
}

bool DirectX::LoadShaders()
{
	if( D3DXCreateEffectFromFile(m_d3ddev,TEXT("../DirectX/SimpleSurface.fx"),0,0,D3DXSHADER_DEBUG,0,&m_SimpleSurfaceShader.Effect,0) != S_OK)
	{
		//LOG(L"Failed to load SimpleSurface\n");
		return false;
	}
	m_SimpleSurfaceShader.Handles.Technique = m_SimpleSurfaceShader.Effect->GetTechniqueByName("tech0");
	m_SimpleSurfaceShader.Effect->SetTechnique(m_SimpleSurfaceShader.Handles.Technique);
	return true;
}


D3DXMATRIX DirectX::BuildProjectionMatrix()
{
	D3DXMATRIX Projection;
	float width = (float)m_d3dpp.BackBufferWidth;
	float height = (float)m_d3dpp.BackBufferHeight;
	D3DXMatrixPerspectiveFovLH(&Projection,D3DX_PI * 0.25f,width/height,1.0f,1024.0f);
	return Projection;
}

ISprite* DirectX::LoadSprite( std::wstring fileName, int frameWidth, int frameHeight )
{
// 	for( std::vector<sprite>::iterator iter(m_sprites.begin()), endIter(m_sprites.end()); iter != endIter; ++iter )
// 	{
// 
// 	}
	m_sprites.push_back(sprite());
	m_sprites.back().m_sprite.LoadTexture( frameWidth, frameHeight ,m_d3ddev, fileName.c_str() );
	m_sprites.back().m_fileName = fileName;
	return &m_sprites.back().m_sprite;
}